﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Web;
using System.Windows.Forms;
using CSScriptLibrary;
using log4net;
using MPMovieCatalog.Configuration;
using MPMovieCatalog.Configuration.Sections;
using MPMovieCatalog.Domain;
using MediaPortal.Video.Database;


namespace MPMovieCatalog.Util
{
    public class FetcherUtils
    {
        private static readonly ILog log = LogManager.GetLogger(typeof(FetcherUtils));

        private static Dictionary<string, IIMDBScriptGrabber> grabbers = new Dictionary<string, IIMDBScriptGrabber>();

        private static IIMDBScriptGrabber GetGrabber(string scriptFileName)
        {
            IIMDBScriptGrabber grabber = null;
            if (!grabbers.TryGetValue(scriptFileName, out grabber))
            {
                string scriptPath = string.Format(@"{0}\scripts\{1}.csscript", Application.StartupPath, scriptFileName);
                if (File.Exists(scriptPath))
                {
                    try
                    {
                        AsmHelper script = new AsmHelper(CSScriptLibrary.CSScript.Load(scriptPath, null, false));
                        grabber = (IIMDBScriptGrabber)script.CreateObject("Grabber");
                    }
                    catch (System.Exception ex)
                    {
                        log.Error(string.Format("Error loading grabber script: {0}, message: {1}", scriptPath, ex.Message), ex);
                    }
                }
                else
                {
                    log.Error(string.Format("Grabber script not found: {0}", scriptPath));
                }

            }
            return grabber;
        }

        public static Dictionary<string, List<FetchedMovie>> FindMovie(string movieTitle)
        {
            Dictionary<string, List<FetchedMovie>> result = new Dictionary<string, List<FetchedMovie>>();
            ConfigurationWrapper configuration = new ConfigurationWrapper();
            foreach (ScriptFile scriptFile in configuration.ScriptSection.Scripts)
            {
                log.Info(string.Format("Search: \"{0}\", grabber: {1}", movieTitle, scriptFile.ToString()));
                IIMDBScriptGrabber grabber = GetGrabber(scriptFile.FileName);
                if (grabber != null)
                {
                    try
                    {
                        string searchString = HttpUtility.UrlEncode(GetSearchString(movieTitle));
                        log.Debug(string.Format("Search string: {0}", searchString));

                        ArrayList foundMovies = new ArrayList();
                        grabber.FindFilm(searchString, scriptFile.Limit, foundMovies);

                        log.Info(string.Format("Search finished, found: {0} result(s)", foundMovies.Count));

                        List<FetchedMovie> movies = new List<FetchedMovie>();
                        foreach (IMDB.IMDBUrl imdbUrl in foundMovies)
                        {
                            FetchedMovie fetchedMovie = new FetchedMovie(scriptFile.FileName, imdbUrl.Title, imdbUrl.URL);
                            movies.Add(fetchedMovie);
                        }

                        result[scriptFile.ToString()] = movies;
                    }
                    catch (System.Exception ex)
                    {
                        log.Error(string.Format("Error searching, message: {1}", ex.Message), ex);
                    }
                }
            }
            return result;
        }

        public static MovieInfo GetMovieInfo(FetchedMovie fetchedMovie)
        {
            log.Info(string.Format("Retrieve details: \"{0}\"", fetchedMovie.Title));
            IIMDBScriptGrabber grabber = GetGrabber(fetchedMovie.Script);
            if (grabber != null)
            {
                try
                {
                    IMDBMovie movie = new IMDBMovie();
                    IMDB.IMDBUrl imdbUrl = new IMDB.IMDBUrl(fetchedMovie.URL, fetchedMovie.Title, fetchedMovie.Script);
                    if (grabber.GetDetails(imdbUrl, ref movie))
                    {
                        MovieInfo movieInfo = new MovieInfo();
                        movieInfo.Name = movie.Title;
                        movieInfo.Year = movie.Year.ToString();
                        movieInfo.Tagline = movie.TagLine;
                        movieInfo.Director = movie.Director;
                        movieInfo.Writer = movie.WritingCredits;
                        movieInfo.IMDBNumber = movie.IMDBNumber;
                        movieInfo.IMDBRating = movie.Rating.ToString();
                        movieInfo.IMDBVotes = movie.Votes;
                        movieInfo.Runtime = movie.RunTime.ToString();
                        movieInfo.MPAARating = movie.MPARating;
                        movieInfo.Description = movie.Plot;

                        if (!string.IsNullOrEmpty(movie.Cast))
                        {
                            foreach (string actorName in movie.Cast.Split('\n'))
                            {
                                Actor actor = new Actor();
                                actor.Name = actorName.Trim();
                                movieInfo.AddActor(actor);
                            }
                        }

                        if (!string.IsNullOrEmpty(movie.Genre))
                        {
                            foreach (string genreName in movie.Genre.Split('/'))
                            {
                                Genre genre = new Genre();
                                genre.Name = genreName.Trim();
                                movieInfo.AddGenre(genre);
                            }
                        }

                        log.Info("Done!");

                        log.Info(string.Format("Download coverart: \"{0}\"...", movie.Title));

                        movieInfo.PictureFileName = MPMovieCatalog.Util.Utils.DownloadImage(movie.ThumbURL);

                        log.Info("Done!");

                        return movieInfo;
                    }
                    else
                    {
                        log.Info("No details found");
                    }
                }
                catch (System.Exception ex)
                {
                    log.Error(string.Format("Error retrieving details, message: {0}", ex.Message), ex);
                }
            }
            return null;
        }

        public static List<ScriptFile> FindAllScripts()
        {
            List<ScriptFile> scripts = new List<ScriptFile>();

            Directory.CreateDirectory(Application.StartupPath + @"\scripts");
            DirectoryInfo di = new DirectoryInfo(Application.StartupPath + @"\scripts");
            FileInfo[] fileList = di.GetFiles("*.csscript", SearchOption.AllDirectories);
            foreach (FileInfo f in fileList)
            {
                try
                {
                    AsmHelper script = new AsmHelper(CSScriptLibrary.CSScript.Load(f.FullName, null, false));
                    IIMDBScriptGrabber grabber = (IIMDBScriptGrabber)script.CreateObject("Grabber");

                    ScriptFile scriptFile = new ScriptFile();
                    scriptFile.Name = grabber.GetName();
                    scriptFile.Language = grabber.GetLanguage();
                    scriptFile.Limit = 10;
                    scriptFile.FileName = Path.GetFileNameWithoutExtension(f.Name);

                    scripts.Add(scriptFile);
                }
                catch (System.Exception ex)
                {
                    log.Error(string.Format("Error reading script file: {0}, message: ", f.FullName, ex.Message), ex);
                }
            }
            return scripts;
        }

        //public List<MoviePicture> FindMoviePictures(string searchString)

        private static void RemoveAllAfter(ref string line, string word)
        {
            int pos = line.IndexOf(word);
            if (pos > 0)
            {
                line = line.Substring(0, pos);
            }
        }

        private static string GetSearchString(string movieTitle)
        {
            string strURL = movieTitle;
            strURL = strURL.ToLower();
            strURL = strURL.Trim();

            // @ 23.09.2004 by FlipGer
            if (strURL.Length <= 7)
            {
                return strURL;
            }
            // END @ | i think it does not make much sense to parse such a short string
            // and i have no problems with x-men on OFDB ;-) and a better result on IMDB with x-men (try out "x men" :-)

            string strTmp = "";
            int ipos = 0;
            int iBracket = 0;
            //bool	bSkip = false;
            for (int i = 0; i < strURL.Length; ++i)
            {
                char kar = strURL[i];
                if (kar == '[' || kar == '(')
                    iBracket++;			//skip everthing between () and []
                else if (kar == ']' || kar == ')')
                    iBracket--;
                else if (iBracket <= 0)
                {
                    // change all non cahrs or digits into ' '
                    if (!Char.IsLetterOrDigit(kar))
                    {
                        kar = ' ';

                    }
                    // skip whitespace at the beginning, only necessary if the "number skipping" is used
                    //if ((kar==' ') && (ipos==0)) continue;

                    // Mars Warrior @ 03-sep-2004.
                    // Check for ' ' and '+' to avoid double or more ' ' and '+' which
                    // mess up the search to the IMDB...
                    if (strTmp.Length == 0)
                    {
                        strTmp += kar;
                        ipos++;
                    }
                    else
                    {
                        if (
                          Char.IsLetterOrDigit(kar) ||
                          (kar == ' ' && strTmp[strTmp.Length - 1] != ' ')
                            //|| (kar == '+' && strTmp[strTmp.Length -1] != '+')
                          )
                        {
                            strTmp += kar;
                            ipos++;
                        }
                    }
                }
            }

            strTmp = strTmp.Trim();

            // Mars Warrior @ 03-sep-2004.
            // The simple line "strTmp.ToLower()" does NOT work. As a result the wrong string
            // (still includes the " dvd" etc. strings) is send to the IMDB causing wrong lookups
            // By changing the line, everything is working MUCH better now ;-)

            RemoveAllAfter(ref strTmp, "divx");
            RemoveAllAfter(ref strTmp, "xvid");
            RemoveAllAfter(ref strTmp, "dvd");
            RemoveAllAfter(ref strTmp, " dvdrip");
            RemoveAllAfter(ref strTmp, "svcd");
            RemoveAllAfter(ref strTmp, "mvcd");
            RemoveAllAfter(ref strTmp, "vcd");
            RemoveAllAfter(ref strTmp, "cd");
            RemoveAllAfter(ref strTmp, "ac3");
            RemoveAllAfter(ref strTmp, "ogg");
            RemoveAllAfter(ref strTmp, "ogm");
            RemoveAllAfter(ref strTmp, "internal");
            RemoveAllAfter(ref strTmp, "fragment");
            RemoveAllAfter(ref strTmp, "proper");
            RemoveAllAfter(ref strTmp, "limited");
            RemoveAllAfter(ref strTmp, "rerip");

            RemoveAllAfter(ref strTmp, "+divx");
            RemoveAllAfter(ref strTmp, "+xvid");
            RemoveAllAfter(ref strTmp, "+dvd");
            RemoveAllAfter(ref strTmp, "+dvdrip");
            RemoveAllAfter(ref strTmp, "+svcd");
            RemoveAllAfter(ref strTmp, "+mvcd");
            RemoveAllAfter(ref strTmp, "+vcd");
            RemoveAllAfter(ref strTmp, "+cd");
            RemoveAllAfter(ref strTmp, "+ac3");
            RemoveAllAfter(ref strTmp, "+ogg");
            RemoveAllAfter(ref strTmp, "+ogm");
            RemoveAllAfter(ref strTmp, "+internal");
            RemoveAllAfter(ref strTmp, "+fragment");
            RemoveAllAfter(ref strTmp, "+proper");
            RemoveAllAfter(ref strTmp, "+limited");
            RemoveAllAfter(ref strTmp, "+rerip");

            // return the new formatted string
            return strTmp;
        }
    }

    public class FetchedMovie
    {
        public FetchedMovie(string script, string title, string url)
        {
            Script = script;
            Title = title;
            URL = url;
        }

        public string Script { get; set; }
        public string Title { get; set; }
        public string URL { get; set; }
    }

    public class MoviePicture
    {
        public MoviePicture(string title, string url)
        {
            Title = title;
            URL = url;
        }

        public MoviePicture(string title, string url, string fileName)
            : this(title, url)
        {
            FileName = fileName;
        }

        public string Title { get; set; }
        public string URL { get; set; }
        public string FileName { get; set; }

        public override string ToString()
        {
            return Title;
        }
    }
}
